;; The first three lines of this file were inserted by DrScheme. They record metadata
;; about the language level of this file in a form that our tools can easily process.
#reader(lib "htdp-intermediate-lambda-reader.ss" "lang")((modname risk) (read-case-sensitive #f) (teachpacks ((lib "universe.ss" "teachpack" "2htdp"))) (htdp-settings #(#f constructor mixed-fraction #f #f none #f ((lib "universe.ss" "teachpack" "2htdp")))))
(require "util-functions.scm")
(require "data.scm")

;; DATA DEFINITIONS CAN BE FOUND IN THE data.scm FILE. THIS FILE CONTAINS
;; FUNCTIONS WHICH MANAGE THE STATE OF THE GAME AT A HIGH LEVEL.

;; Lower level, more generic tasks are delegated to the util-functions.scm file.

;;
;; @advancementfunctions
;; World Advancement functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



;; next-world : board → board
;; advances this world to the next one on tick event.
;; we take no action on a tick event. The game is entirely driven by 
;; player input.
(define (next-world board)
  board)

;; next-state : board -> board
;; advances the state, and nothing else.
(define (next-state board)
  (local [(define phase (car (board-state board)))]
    (cond [(symbol=? phase 'init) (make-board (board-territories board)
                                              (board-continents board)
                                              (board-players board)
                                              (list 'p1 empty empty))]
          [(symbol=? phase 'p1) (make-board (board-territories board) 
                                              (board-continents board)
                                              (board-players board)
                                              (list 'p2-origin empty empty))]
          [(or (symbol=? phase 'p2-origin)
               (symbol=? phase 'p2-victim)) (make-board (board-territories board)
                                            (board-continents board)
                                            (board-players board)
                                            (list 'p3-origin empty empty))]
          [(or (symbol=? phase 'p3-dest)
               (symbol=? phase 'p3-origin)) (phase1-begin board)])))
  
#|(check-expect (next-state (make-board '() '() '() (list 'init empty empty empty)))
              (make-board '() '() '() (list 'p1 empty empty empty)))
(check-expect (next-state board-blank)
              (make-board territory-list-base
                          continent-list
                          g-players
                          (list 'p2-origin empty empty)))
(check-expect (next-state board-blank-state2)
              (make-board territory-list-base
                          continent-list
                          g-players
                          (list 'p3-origin empty empty phase3-origin-buttons)))
(check-expect (next-state running-game-p3-o)
              (make-board g-territory-list-current
                                      continent-list
                                      '(blue red)
                                      (list 'p1 empty empty empty)))
(check-expect (next-state running-game-p3-d-0)
              (make-board g-territory-list-current
                                      continent-list
                                      '(blue red)
                                      (list 'p1 empty empty empty)))|#
  

;;
;; END OF
;; World Advancement functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



;; handle-ui-clicked : ui-element board -> board
;; handles the action that should be taken based on the given territory being selected by a
;; player who is in the state given by the board.
(define (handle-ui-clicked ui-element board)
  (local [(define state (car (board-state board)))]
         (cond [(symbol=? state 'init) (initialize-board board)]
               [(symbol=? state 'p1) (phase1-handler ui-element board)]
               [(symbol=? state 'p2-origin) (phase2-origin-handler ui-element board)]
               [(symbol=? state 'p2-victim) (phase2-victim-handler ui-element board)]
               [(symbol=? state 'p3-origin) (phase3-origin-handler ui-element board)]
               [(symbol=? state 'p3-dest) (phase3-destination-handler ui-element board)])))

(check-expect (handle-ui-clicked g-ter-sa g-board-p1)
              (phase1-handler g-ter-sa g-board-p1))
(check-expect (handle-ui-clicked g-ter-sa running-game-p2-o)
              (phase2-origin-handler g-ter-sa running-game-p2-o))
(check-expect (handle-ui-clicked g-ter-sa running-game-p2-victim)
              (phase2-victim-handler g-ter-sa running-game-p2-victim))
(check-expect (handle-ui-clicked g-ter-sa running-game-p3-o)
              (phase3-origin-handler g-ter-sa running-game-p3-o))
(check-expect (handle-ui-clicked g-ter-sa running-game-p3-d-0)
              (phase3-destination-handler g-ter-sa running-game-p3-d-0)) 


;;
;; @phase1functions
;; Phase 1 Functions
;;;;;;;;;;;;;;;;;;;;;

;; calculate-reenforcements : Player board → number
;; calculates the number of reenforcements that the given player
;; should receive (total territories / 3) (or 3) + continent bonuses
(define (calculate-reenforcements s b)
  (local [(define terrs (territories-owned s (board-territories b)))
          (define cont-reenf
            (continent-reenforcement (continents-owned s
                                                       (board-territories b)
                                                       (board-continents b))))]
         (cond [(<= terrs 9) (+ 3 cont-reenf)]
               [else (+ (quotient terrs 3) cont-reenf)])))

(check-expect (calculate-reenforcements 'red g-board-p1) 5)
(check-expect (calculate-reenforcements 'unowned board-blank) 11)

;; continent-reenforcement : [listof Continent] → number
;; calculates the number of reenforcements due to continent ownership
(define (continent-reenforcement loc)
  (cond [(empty? loc) 0]
        [else (+ (continent-value (first loc))
                 (continent-reenforcement (rest loc)))]))

(check-expect (continent-reenforcement continent-list) 7)

;; continent-reenforcements : [Listof Continent] Number -> Number
;; returns the total amount to be added to the board
;; determined by all the continents owned.
;; @needstests
(define (continent-reenforcements loc acc)
  (cond [(empty? loc) acc]
        [else (continent-reenforcements (cdr loc)
                                        (+ acc (continent-reenforcement (car loc))))]))


;; phase1-handler : ui-element board → board
;; adds an army to the given ui-element (if the ui-element is a Territory that the active player owns)
;; or else return the given board
;; phase1 has no buttons that could be pressed...
;; @needstests
(define (phase1-handler ui-element board)
  (cond [(zero? (second (board-state board))) (next-state board)]
        [(territory-owned? (current-player board) ui-element) (place-reenforcement ui-element board)]
        [else board]))

;; place-reenforcement : Territory board -> board
;; places a reenforcement on the given territory on the game board.
;; @needstests
(define (place-reenforcement ter board)
  (local ((define new-territory
            (make-territory (territory-name ter)
                            (territory-continent ter)
                            (add1 (territory-units ter))
                            (territory-neighbors ter)
                            (territory-player ter))))
         (make-board (replace-territory (board-territories board) new-territory)
                     (board-continents board)
                     (board-players board)
                     (list 'p1 (sub1 (sds1 board)) empty empty))))

(check-expect (place-reenforcement ter-usa ter-update-board) (make-board (list ter-canada ter-usa-plus1)
                                                                         empty
                                                                         empty
                                                                         (list 'p1 9 empty empty)))
(check-expect (place-reenforcement ter-canada ter-update-board) (make-board (list ter-canada-plus1 ter-usa)
                                                                             empty
                                                                             empty
                                                                             (list 'p1 9 empty empty)))
;; phase1-begin : board -> board
;; begins phase 1 from the end of phase 3, determining how many elements are to be
;; placed onto the game board.
;; @needstests
(define (phase1-begin board)
  (make-board (board-territories board)
              (board-continents board)
              (cons (get-last (board-players board))
                    (remove-last (board-players board)))
              (list 'p1
                    (calculate-reenforcements (get-last (board-players board)) board)
                    empty
                    empty)))


;;
;; END OF
;; Phase 1 Functions
;;;;;;;;;;;;;;;;;;;;;

;;
;; @phase2functions
;; Phase 2 Functions
;;;;;;;;;;;;;;;;;;;;;

;; phase2-origin-handler : ui-element board → board
;; makes the given ui-element the origin of the attack (if it is a territory owned by
;; the active player) or moves to the next phase if the player clicked the next-phase button
;; otherwise does nothing
(define (phase2-origin-handler ui-element board)
  (cond ;; the only button is the next-phase button
    [(and (symbol? ui-element) (symbol=? 'next-phase ui-element))
          (change-board-state board g-state-p3-o)]
    [(and (territory? ui-element)
          (territory-owned? (current-player board)
                            ui-element))
     (change-board-state board
                         (list 'p2-victim
                               ui-element
                               empty
                               ))]
    [else board]))

#|(check-expect (phase2-origin-handler next-phase running-game-p2-o)
              running-game-p3-o)
(check-expect (phase2-origin-handler g-ter-germany running-game-p2-o)
              (make-board g-territory-list-current
                          continent-list
                          g-players
                          (list 'p2-victim
                                g-ter-germany
                                empty
                                (list next-phase cancel))))
(check-expect (phase2-origin-handler g-ter-france running-game-p2-o)
              running-game-p2-o)|#

;; phase2-victim-handler : UI-Element Board → Board
;; makes the given ui-element the victim of the attack (if it is a territory not owned by
;; the active player and is adjacent to the origin of the attack), or cancels the attack if
;; the player hit the cancel button, OR does nothing
;; @needstests
(define (phase2-victim-handler ui-element board)
  (cond [(symbol? ui-element) (phase2-victim-handler-buttons ui-element
                                                             board)]
        [(territory? ui-element) (phase2-victim-handler-territories ui-element
                                                                    board)]))

#|(check-expect (phase2-victim-handler cancel running-game-p2-victim)
              running-game-p2-victim)
(check-expect (phase2-victim-handler next-phase running-game-p2-victim)
              running-game-p3-o)
(check-expect (phase2-victim-handler g-ter-germany running-game-p2-victim)
              running-game-p2-victim)|#

;; phase2-victim-handler-buttons : Symbol Board -> Board
;; modifies the board appropriately depending upon which button was pressed
;; button - action
;; cancel - reset the state to phase2-origin and clear the origin territory
;; next-phase - skips the remainder of this phase (no more attacking) and goes
;;   to the next phase
(define (phase2-victim-handler-buttons button board)
  (cond [(symbol=? button 'next-phase) (next-state board)]
        [(symbol=? button 'cancel)
         (change-board-state board g-state-p2-o)]
        [else board]))

#;(check-expect (phase2-victim-handler-buttons (make-button 'BULLSHIT!
                                                          (make-widget 0 0
                                                                       (rectangle 100
                                                                                  100
                                                                                  'solid
                                                                                  'green)))
                                             board-blank)
              board-blank)

#|(check-expect (phase2-victim-handler-buttons cancel running-game-p2-victim)
              running-game-p2-o)
(check-expect (phase2-victim-handler-buttons next-phase
                                             running-game-p2-victim)
              running-game-p3-o)|#

;; phase2-victim-handler-territories Territory Board -> Board
;; commences an attack if a valid (not owned by the attacker and adjacent
;; to the origin) victim territory was clicked otherwise loops in this phase
;; without changing anything
;; @brokenimagetest
(define (phase2-victim-handler-territories territory board)
  (cond [(and (not (territory-owned? (current-player board) territory))
              (adjacent? territory (sds1 board))
              (> (territory-units (sds1 board)) 1))
         (change-board-state (map-terr-list (attack-territory (sds1 board) (pred1 (territory-units (sds1 board)))
                                                              territory (if (and (not (= (pred1 (territory-units (sds1 board))) 2))
                                                                                 (>= (territory-units territory) 2)) 2 1))
                                            board)
                             g-state-p2-o)]  
        [else board]))


(check-expect (phase2-victim-handler-territories g-ter-germany
                                                 running-game-p2-victim)
              running-game-p2-victim)
;; broken because the images don't line up
#;
(check-expect (phase2-victim-handler-territories g-ter-france
                                                 running-game-p2-victim)
              (make-board (list g-ter-canada g-ter-usa g-ter-mexico g-ter-sa
                                (make-territory 'france 'europe 4 adj-france
                                                'blue widget-france)
                                g-ter-germany g-ter-mideast
                                g-ter-africa-north g-ter-africa-south
                                g-ter-russia g-ter-china g-ter-australia)
                          continent-list
                          g-players
                          g-state-p2-o))

;; attack-territory : Territory Number Territory Number -> [Listof Territory]
;; Performs actual, working, risk-like attacks.
(define (attack-territory terr1 max1 terr2 max2)
  (local [(define a-dice (sort (build-list max1 (lambda (x) (random 6))) >))
          (define d-dice (sort (build-list max2 (lambda (x) (random 6))) >))]
    (cond [(= max2 1) (if (> (first a-dice) (first d-dice))
                          (determine-victor (list terr1 (change-territory-units/territory terr2 (sub1 (territory-units terr2)))))
                          (list (change-territory-units/territory terr1 (sub1 (territory-units terr1))) terr2))]
          [else (regular-attack-calc a-dice d-dice terr1 terr2)])))

(define (regular-attack-calc a-dice d-dice terr1 terr2) 
  (cond [(pred-t2-sub2 a-dice d-dice)
         (determine-victor (list terr1 (change-territory-units/territory terr2 (sub2 (territory-units terr2)))))]
        [(pred-1-and-1 a-dice d-dice)
         (determine-victor (list (change-territory-units/territory terr1 (sub1 (territory-units terr1)))
                                 (change-territory-units/territory terr2 (sub1 (territory-units terr2)))))]
        [(pred-t1-sub2 a-dice d-dice)
         (determine-victor (list (change-territory-units/territory terr1 (sub2 (territory-units terr1))) terr2))]))

;; THIS TEST FAILS!!!!!  The shapes are missing
#;
(check-expect (attack-territory g-ter-africa-north g-ter-mideast)
              (list g-ter-mideast-post-attack g-ter-africa-north-post-attack))

;; determine-victor : [ListOf Territory] -> [ListOf Territory]
;; determines if there is a territory change as a result of the attack
(define (determine-victor terrs)
  (local [(define terr1 (first terrs))
          (define terr2 (second terrs))]
    (cond [(= 0 (territory-units terr2))
           (list (change-territory-units/territory
                 (change-territory-owner/territory terr2 (territory-player terr1))
                 (sub1 (territory-units terr1)))
               (change-territory-units/territory terr1 1))]
          [else terrs])))
           

;;
;; END OF
;; Phase 2 Functions
;;;;;;;;;;;;;;;;;;;;;

;;
;; @phase3functions
;; Phase 3 Functions
;;;;;;;;;;;;;;;;;;;;

;; phase3-origin-handler : ui-element board -> board
;; makes the given ui-element the origin of the reenforcement move (if it is a territory owned
;; by the active player) or moves to the next phase if the player clicked the next-phase button
;; otherwise, does nothing
(define (phase3-origin-handler ui-element board)
  (cond [(and (territory? ui-element)
              (territory-owned? (first (board-players board)) ui-element))
         (make-board (disperse ui-element board)
                     (board-continents board)
                     (board-players board)
                     (list 'p3-dest
                           ui-element
                           (sub1 (territory-units ui-element))
                           empty))]
        [(and (symbol? ui-element) (symbol=? ui-element 'next-phase))
         (next-state board)]
        [else board]))

#|(check-expect (phase3-origin-handler 'boogady board-blank) board-blank)
(check-expect (phase3-origin-handler next-phase running-game-p3-o) (make-board g-territory-list-current
                                                                               continent-list
                                                                               '(blue red)
                                                                               (list 'p1 empty empty empty)))
(check-expect (phase3-origin-handler g-ter-germany running-game-p3-o) (make-board g-territory-list-current
                                                                                  continent-list
                                                                                  g-players
                                                                                  (list 'p3-dest
                                                                                        g-ter-germany
                                                                                        4
                                                                                        empty)))|#

;; phase3-destination-handler : ui-element board -> board
;; makes the given ui-element the territory to be reenforced and reenforces it (if it is a territory
;; owned by the active player with a direct line to the originating territory) or moves to the next
;; phase if the player clicked the next-phase button (the next phase being the setup for phase1 for
;; the next plater to take a turn)
;; otherwise, does nothing
;; @needstests
(define (phase3-destination-handler ui-element board)
  (cond [(and (not (empty? (sds2 board)))
              (zero? (sds2 board))) (next-state board)]
        [(and (territory-owned? (first (board-players board)) ui-element)
              (or (is-neighbor? (territory-neighbors (sds1 board)) ui-element)
                  (symbol=? (territory-name ui-element) (territory-name (sds1 board)))))
         (place-reenforcement-p3 ui-element board)]
        [else board]))

(check-expect (phase3-destination-handler 'boogady running-game-p3-d-0) (make-board (board-territories running-game-p3-d-0)
                                                                                    (board-continents running-game-p3-d-0)
                                                                                    '(blue red)
                                                                                    (list 'p1 empty empty empty)))

;; place-reenforcement-p3 : Territory board -> board
;; places a reenforcement on the given territory on the game board, DURING PHASE 3.
;; @needstests
(define (place-reenforcement-p3 ter board)
  (local ((define new-territory
            (make-territory (territory-name ter)
                            (territory-continent ter)
                            (add1 (territory-units ter))
                            (territory-neighbors ter)
                            (territory-player ter))))
         (make-board (replace-territory (board-territories board) new-territory)
                     (board-continents board)
                     (board-players board)
                     (list 'p3-dest (sds1 board) (sub1 (sds2 board)) empty))))

 
;;
;; END OF
;; Phase 3 Functions
;;;;;;;;;;;;;;;;;;;;;

;;
;; @initfunctions
;; Initilization Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; initialize-board : board -> board
;; initializes the game board, by randomly assigning continents to players and putting a certain
;; number of armies on each territory that they control. (Ends by advancing to phase 1 for the
;; first player.)
;; @needstests
(define (initialize-board board)
  (phase1-begin (make-board
                  (init-board-acc (board-territories board) (board-players board) '())
                  (board-continents board)
                  (board-players board)
                  (board-state board))))

;; init-board-acc : [Listof territories] int [Listof territories] -> [Listof territories]
;; initializes the board by calling helping fuctions and using accumluators
;; @needstests
(define (init-board-acc ter-list list-plyrs terr-acc)
  (cond [(= (length ter-list) 0) terr-acc]
        [else (local [(define size (length ter-list))
                      (define rand (random size))
                      (define plyr (list-ref list-plyrs (modulo size (length list-plyrs))))
                      (define terr  (list-ref ter-list rand))
                      (define new-ter (set-player terr plyr))]
                     (init-board-acc (my-remove terr ter-list)
                                     list-plyrs
                                     (cons new-ter terr-acc)))]))
;; @needscontract @needstests
(define (my-remove ter ter-list)
  (cond [(eq? ter (car ter-list)) (cdr ter-list)]
        [else (cons (car ter-list) (my-remove ter (cdr ter-list)))]))

(check-expect (my-remove ter-canada territory-list-base)
              (list ter-usa ter-mexico ter-sa ter-france ter-germany
                    ter-mideast ter-africa-north ter-africa-south ter-russia
                    ter-china ter-australia))

;;
;; END OF
;; Initilization Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;
;; @bigbang
;; The Big Bang and associated functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; The number of maximum players for the game.
(define BASE-MAXIMUM-PLAYERS 2)

;; new-client : [listof world] universe world -> bundle
;; handles a new client connecting to this universe.
(define (new-client low uni new-world)
  (cond ;[(>= (num-current-players uni) BASE-MAXIMUM-PLAYERS) (make-bundle low uni empty)]
        [else (make-bundle (cons new-world low) uni empty)]))
         #;(local ((define new-player (make-new-player (num-current-players uni)))
                      (define new-low (cons new-world low))
                      (define new-uni (add-player new-player uni))
                      (define new-player-msg (msg-new-player new-player new-world))
                      (define msg-rest (update-all low new-uni)))
                (make-bundle new-low new-uni (cons new-player-msg msg-rest)))

;; new-message : [listof world] universe world sexp -> bundle
;; handles receiving a new message from the given client.
(define (new-message low uni world msg)
  (cond [(not (valid-msg? msg))
         (make-bundle low uni empty)]
        [(iworld=? (first low) world)
         (local [(define territory-received (lookup-territory (first msg) (board-territories uni)))
                 (define msg-received (if (boolean? territory-received) (first msg) territory-received))
                 (define current-player (first (board-players uni)))
                 (define new-uni (handle-ui-clicked msg-received uni))
                 (define new-mails (update-all low new-uni))
                 (define next-player (first (board-players new-uni)))
                 (define next-low (if (symbol=? current-player next-player)
                                      low
                                      (append (rest low) (list (first low)))))]
           (make-bundle next-low new-uni new-mails))]
        [else (make-bundle low uni empty)]))

;; update-all : [listof world] universe -> [listof mail]
;; makes a list of mails containing the state of te universe to all worlds
;; connected to this universe.
(define (update-all low uni)
  (cond [(empty? low) empty]
        [else (cons (make-mail (first low) uni)
                    (update-all (rest low) uni))]))


;; valid-msg : sexp -> boolean
;; returns true if the message is valid. ONLY checks the message body itself
;; for validity; does not actually determine if the message was sent by the
;; current player.
(define (valid-msg? msg)
  (and (cons? msg)
       (empty? (rest msg))
       (symbol? (first msg))))


;; This piece of code actually starts the server.
(universe g-board-p1
          (on-new new-client)
          (on-msg new-message))





;;
;; END OF
;; The Big Bang and associated functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
